{*******************************************************}
{                                                       }
{       Borland Delphi Visual Component Library         }
{                                                       }
{       Copyright (c) 1996,99 Inprise Corporation       }
{                                                       }
{*******************************************************}

unit Richedit2;

interface

uses Messages, Windows, SysUtils, Classes, Controls, Forms,
  Graphics, StdCtrls, RichEdit, ComCtrls;

type
  TTabAlignment = (tabLeftAlignment, tabCenterAlignment, tabRightAlignment, tabDecimal{, tabBarre});
  TNsTypePuce = (tpNone, tpBullet, tpChiffreArabe, tpMinuscule, tpMajuscule, tpRomainMin, tpRomainMaj);
  TNsCadrePuce = (cpParentheseD, cpParenthesesGD, cpPoint, cpRien);

  TNumberingStyle2 = packed record
    TypePuce: TNsTypePuce;
    CadrePuce: TNsCadrePuce;
  end;

  TCustomRichEdit2 = class;

{ TTextAttributes }

  //Lorsque ce define n'est pas définie, le compilateur C détecte une ambigüité entre CHARFORMAT2A et richedit::CHARFORMAT2A
  (*$HPPEMIT '#define CHARFORMAT2A ::CHARFORMAT2A'*)

  TTextAttributes = class(TPersistent)
  private
    RichEdit: TCustomRichEdit2;
    FType: TAttributeType;
    procedure GetAttributes(var Format: CHARFORMAT2);
    function GetCharset: TFontCharset;
    function GetColor: TColor;
    function GetConsistentAttributes: TConsistentAttributes;
    function GetHeight: Integer;
    function GetName: TFontName;
    function GetPitch: TFontPitch;
    function GetProtected: Boolean;
    function GetSize: Integer;
    function GetStyle: TFontStyles;
    procedure SetAttributes(var Format: CHARFORMAT2);
    procedure SetCharset(Value: TFontCharset);
    procedure SetColor(Value: TColor);
    procedure SetHeight(Value: Integer);
    procedure SetName(Value: TFontName);
    procedure SetPitch(Value: TFontPitch);
    procedure SetProtected(Value: Boolean);
    procedure SetSize(Value: Integer);
    procedure SetStyle(Value: TFontStyles);
  protected
    procedure InitFormat(var Format: CHARFORMAT2);
    procedure AssignTo(Dest: TPersistent); override;
  public
    constructor Create(AOwner: TCustomRichEdit2; AttributeType: TAttributeType);
    procedure Assign(Source: TPersistent); override;
    procedure AddStyle(Style: TFontStyle);
    procedure RemoveStyle(Style :TFontStyle);
    property Charset: TFontCharset read GetCharset write SetCharset;
    property Color: TColor read GetColor write SetColor;
    property ConsistentAttributes: TConsistentAttributes read GetConsistentAttributes;
    property Name: TFontName read GetName write SetName;
    property Pitch: TFontPitch read GetPitch write SetPitch;
    property Protected: Boolean read GetProtected write SetProtected;
    property Size: Integer read GetSize write SetSize;
    property Style: TFontStyles read GetStyle write SetStyle;
    property Height: Integer read GetHeight write SetHeight;
  end;

{ TParaAttributes }

  TParaAttributes = class(TPersistent)
  private
    RichEdit: TCustomRichEdit2;
    procedure GetAttributes(var Paragraph: PARAFORMAT2);
    function GetAlignment: TAlignment;
    function GetFirstIndent: Longint;
    function GetLeftIndent: Longint;
    function GetRightIndent: Longint;
    function GetTab(Index: Byte): Longint;
    function GetTabAlignment(Index: Byte): TTabAlignment;
    function GetTabCount: Integer;
    procedure InitPara(var Paragraph: PARAFORMAT2);
    procedure SetAlignment(Value: TAlignment);
    procedure SetAttributes(var Paragraph: PARAFORMAT2);
    procedure SetFirstIndent(Value: Longint);
    procedure SetLeftIndent(Value: Longint);
    procedure SetRightIndent(Value: Longint);
    procedure SetTab(Index: Byte; Value: Longint);
    procedure SetTabAlignment(Index: Byte; Value: TTabAlignment);
    procedure SetTabCount(Value: Integer);
  public
    constructor Create(AOwner: TCustomRichEdit2);
    procedure Assign(Source: TPersistent); override;
    property Alignment: TAlignment read GetAlignment write SetAlignment;
    property FirstIndent: Longint read GetFirstIndent write SetFirstIndent;
    property LeftIndent: Longint read GetLeftIndent write SetLeftIndent;
    function GetNumbering: TNumberingStyle2;
    procedure SetNumbering(Value: TNumberingStyle2);
    property RightIndent: Longint read GetRightIndent write SetRightIndent;
    property Tab[Index: Byte]: Longint read GetTab write SetTab;
    property TabAlignment[Index: Byte]: TTabAlignment read GetTabAlignment write SetTabAlignment;
    property TabCount: Integer read GetTabCount write SetTabCount;
  end;

{ TCustomRichEdit2 }
  TCustomRichEdit2 = class(TCustomMemo)
  private
    FHideScrollBars: Boolean;
    FSelAttributes: TTextAttributes;
    FDefAttributes: TTextAttributes;
    FParagraph: TParaAttributes;
    FOldParaAlignment: TAlignment;
    FScreenLogPixels: Integer;
    FRichEditStrings: TStrings;
    FMemStream: TMemoryStream;
    FOnSelChange: TNotifyEvent;
    FHideSelection: Boolean;
    FModified: Boolean;
    FDefaultConverter: TConversionClass;
    FOnResizeRequest: TRichEditResizeEvent;
    FOnProtectChange: TRichEditProtectChange;
    FOnSaveClipboard: TRichEditSaveClipboard;
    FPageRect: TRect;
    procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED;
    procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
    procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
    function GetPlainText: Boolean;
    function ProtectChange(StartPos, EndPos: Integer): Boolean;
    function SaveClipboard(NumObj, NumChars: Integer): Boolean;
    procedure SetHideScrollBars(Value: Boolean);
    procedure SetHideSelection(Value: Boolean);
    procedure SetPlainText(Value: Boolean);
    procedure SetRichEditStrings(Value: TStrings);
    procedure SetDefAttributes(Value: TTextAttributes);
    procedure SetSelAttributes(Value: TTextAttributes);
    procedure WMNCDestroy(var Message: TWMNCDestroy); message WM_NCDESTROY;
    procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure WMSetFont(var Message: TWMSetFont); message WM_SETFONT;
    procedure WMRButtonUp(var Message: TWMRButtonUp); message WM_RBUTTONUP;
    function GetText: string;
    procedure SetText(Value: string);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure CreateWnd; override;
    procedure DestroyWnd; override;
    procedure RequestSize(const Rect: TRect); virtual;
    procedure SelectionChange; dynamic;
    procedure DoSetMaxLength(Value: Integer); override;
    function GetCaretPos: TPoint; override;
    function GetSelLength: Integer; override;
    function GetSelStart: Integer; override;
    function GetSelText: string; override;
    procedure SetSelText(const Value: string); 
    procedure SetSelLength(Value: Integer); override;
    procedure SetSelStart(Value: Integer); override;
    property HideSelection: Boolean read FHideSelection write SetHideSelection default True;
    property HideScrollBars: Boolean read FHideScrollBars
      write SetHideScrollBars default True;
    property Lines: TStrings read FRichEditStrings write SetRichEditStrings;
    property OnSaveClipboard: TRichEditSaveClipboard read FOnSaveClipboard
      write FOnSaveClipboard;
    property OnSelectionChange: TNotifyEvent read FOnSelChange write FOnSelChange;
    property OnProtectChange: TRichEditProtectChange read FOnProtectChange
      write FOnProtectChange;
    property OnResizeRequest: TRichEditResizeEvent read FOnResizeRequest
      write FOnResizeRequest;
    property PlainText: Boolean read GetPlainText write SetPlainText default False;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Clear; override;
    function FindTextA(const SearchStr: string;
      StartPos, LengthText: Integer; Options: TSearchTypes): Integer;
    function GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer; override;
    procedure Print(const Caption: string); virtual;
    class procedure RegisterConversionFormat(const AExtension: string;
      AConversionClass: TConversionClass);
    property DefaultConverter: TConversionClass
      read FDefaultConverter write FDefaultConverter;
    property DefAttributes: TTextAttributes read FDefAttributes write SetDefAttributes;
    property SelAttributes: TTextAttributes read FSelAttributes write SetSelAttributes;
    property PageRect: TRect read FPageRect write FPageRect;
    property Paragraph: TParaAttributes read FParagraph;
    property SelText: string read GetSelText write SetSelText;
	property Text : string read GetText write SetText stored false; 
  end;

  TRichEdit2 = class(TCustomRichEdit2)
  published
    property Align;
    property Alignment;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property BorderWidth;
    property Color;
    property Ctl3D;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property HideSelection;
    property HideScrollBars;
    property ImeMode;
    property ImeName;
    property Constraints;
    property Lines;
    property MaxLength;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PlainText;
    property PopupMenu;
    property ReadOnly;
    property ScrollBars;
    property ShowHint;
    property TabOrder;
    property TabStop default True;
    property Visible;
    property WantTabs;
    property WantReturns;
    property WordWrap;
    property OnChange;
    property OnContextPopup;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnProtectChange;
    property OnResizeRequest;
    property OnSaveClipboard;
    property OnSelectionChange;
    property OnStartDock;
    property OnStartDrag;
  end;

const taJustify = TAlignment(3); //Pour étoffer le type TAlignment. En effet, taJustify est disponible à partir du RichEdit2

implementation

uses Printers, ComStrs, CommDlg, Dialogs;

{$DEFINE RICHEDIT_UNICODE}

const
  EM_SETTYPOGRAPHYOPTIONS = WM_USER + 202;
  TO_ADVANCEDTYPOGRAPHY   = 1;
  RTFConversionFormat: TConversionFormat = (
    ConversionClass: TConversion;
    Extension: 'rtf';
    Next: nil);
  TextConversionFormat: TConversionFormat = (
    ConversionClass: TConversion;
    Extension: 'txt';
    Next: @RTFConversionFormat);

var
  ConversionFormatList: PConversionFormat = @TextConversionFormat;
{$IFNDEF RICHEDIT_UNICODE}
  FRichEdit20Module: THandle;
{$ELSE}
  FMsftEditModule:   THandle;
{$ENDIF}


{ TTextAttributes }

constructor TTextAttributes.Create(AOwner: TCustomRichEdit2;
  AttributeType: TAttributeType);
begin
  inherited Create;
  RichEdit := AOwner;
  FType := AttributeType;
end;

procedure TTextAttributes.InitFormat(var Format: CHARFORMAT2);
begin
  FillChar(Format, SizeOf(CHARFORMAT2), 0);
  Format.cbSize := SizeOf(CHARFORMAT2);
end;

function TTextAttributes.GetConsistentAttributes: TConsistentAttributes;
var
  Format: CHARFORMAT2;
begin
  Result := [];
  if RichEdit.HandleAllocated and (FType = atSelected) then
  begin
    InitFormat(Format);
    SendMessage(RichEdit.Handle, EM_GETCHARFORMAT,
      WPARAM(FType = atSelected), LPARAM(@Format));
    with Format do
    begin
      if (dwMask and CFM_BOLD) <> 0 then Include(Result, caBold);
      if (dwMask and CFM_COLOR) <> 0 then Include(Result, caColor);
      if (dwMask and CFM_FACE) <> 0 then Include(Result, caFace);
      if (dwMask and CFM_ITALIC) <> 0 then Include(Result, caItalic);
      if (dwMask and CFM_SIZE) <> 0 then Include(Result, caSize);
      if (dwMask and CFM_STRIKEOUT) <> 0 then Include(Result, caStrikeOut);
      if (dwMask and CFM_UNDERLINE) <> 0 then Include(Result, caUnderline);
      if (dwMask and CFM_PROTECTED) <> 0 then Include(Result, caProtected);
    end;
  end;
end;

procedure TTextAttributes.GetAttributes(var Format: CHARFORMAT2);
begin
  InitFormat(Format);
  if RichEdit.HandleAllocated then
    SendMessage(RichEdit.Handle, EM_GETCHARFORMAT,
      WPARAM(FType = atSelected), LPARAM(@Format));
end;

procedure TTextAttributes.SetAttributes(var Format: CHARFORMAT2);
var
  Flag: Longint;
begin
  if FType = atSelected then Flag := SCF_SELECTION
  else Flag := 0;
  if RichEdit.HandleAllocated then
    SendMessage(RichEdit.Handle, EM_SETCHARFORMAT, Flag, LPARAM(@Format))
end;

function TTextAttributes.GetCharset: TFontCharset;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  Result := Format.bCharset;
end;

procedure TTextAttributes.SetCharset(Value: TFontCharset);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_CHARSET;
    bCharSet := Value;
  end;
  SetAttributes(Format);
end;

function TTextAttributes.GetProtected: Boolean;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  with Format do
    if (dwEffects and CFE_PROTECTED) <> 0 then
      Result := True else
      Result := False;
end;

procedure TTextAttributes.SetProtected(Value: Boolean);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_PROTECTED;
    if Value then dwEffects := CFE_PROTECTED;
  end;
  SetAttributes(Format);
end;

function TTextAttributes.GetColor: TColor;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  with Format do
    if (dwEffects and CFE_AUTOCOLOR) <> 0 then
      Result := clWindowText else
      Result := crTextColor;
end;

procedure TTextAttributes.SetColor(Value: TColor);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_COLOR;
    if Value = clWindowText then
      dwEffects := CFE_AUTOCOLOR else
      crTextColor := ColorToRGB(Value);
  end;
  SetAttributes(Format);
end;

function TTextAttributes.GetName: TFontName;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  Result := Format.szFaceName;
end;

procedure TTextAttributes.SetName(Value: TFontName);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_FACE;
    StrPLCopy(szFaceName, Value, SizeOf(szFaceName));
  end;
  SetAttributes(Format);
end;

procedure TTextAttributes.AddStyle(Style :TFontStyle);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);

  if (Style = fsBold) then
     begin
     Format.dwMask := CFM_BOLD;
     Format.dwEffects := Format.dwEffects or CFE_BOLD;
     end
  else if (Style = fsItalic) then
     begin
     Format.dwMask := CFE_ITALIC;
     Format.dwEffects := Format.dwEffects or CFE_ITALIC;
     end
  else if (Style = fsUnderline) then
     begin
     Format.dwMask := CFE_UNDERLINE;
     Format.dwEffects := Format.dwEffects or CFE_UNDERLINE;
     end
  else if (Style = fsStrikeOut) then
     begin
     Format.dwMask := CFE_STRIKEOUT;
     Format.dwEffects := Format.dwEffects or CFE_STRIKEOUT;
     end;

  SetAttributes(Format);
end;

procedure TTextAttributes.RemoveStyle(Style: TFontStyle);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);

  if (Style = fsBold) then
     begin
     Format.dwMask := CFM_BOLD;
     Format.dwEffects := Format.dwEffects and not CFE_BOLD;
     end
  else if (Style = fsItalic) then
     begin
     Format.dwMask := CFE_ITALIC;
     Format.dwEffects := Format.dwEffects and not CFE_ITALIC;
     end
  else if (Style = fsUnderline) then
     begin
     Format.dwMask := CFE_UNDERLINE;
     Format.dwEffects := Format.dwEffects and not CFE_UNDERLINE;
     end
  else if (Style = fsStrikeOut) then
     begin
     Format.dwMask := CFE_STRIKEOUT;
     Format.dwEffects := Format.dwEffects and not CFE_STRIKEOUT;
     end;

  SetAttributes(Format);
end;


function TTextAttributes.GetStyle: TFontStyles;
var
  Format: CHARFORMAT2;
begin
  Result := [];
  GetAttributes(Format);
  with Format do
  begin
    if (dwEffects and CFE_BOLD) <> 0 then Include(Result, fsBold);
    if (dwEffects and CFE_ITALIC) <> 0 then Include(Result, fsItalic);
    if (dwEffects and CFE_UNDERLINE) <> 0 then Include(Result, fsUnderline);
    if (dwEffects and CFE_STRIKEOUT) <> 0 then Include(Result, fsStrikeOut);
  end;
end;

procedure TTextAttributes.SetStyle(Value: TFontStyles);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_BOLD or CFM_ITALIC or CFM_UNDERLINE or CFM_STRIKEOUT;
    if fsBold in Value then dwEffects := dwEffects or CFE_BOLD;
    if fsItalic in Value then dwEffects := dwEffects or CFE_ITALIC;
    if fsUnderline in Value then dwEffects := dwEffects or CFE_UNDERLINE;
    if fsStrikeOut in Value then dwEffects := dwEffects or CFE_STRIKEOUT;
  end;
  SetAttributes(Format);
end;

function TTextAttributes.GetSize: Integer;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  Result := Format.yHeight div 20;
end;

procedure TTextAttributes.SetSize(Value: Integer);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := DWORD(CFM_SIZE);
    yHeight := Value * 20;
  end;
  SetAttributes(Format);
end;

function TTextAttributes.GetHeight: Integer;
begin
  Result := MulDiv(Size, RichEdit.FScreenLogPixels, 72);
end;

procedure TTextAttributes.SetHeight(Value: Integer);
begin
  Size := MulDiv(Value, 72, RichEdit.FScreenLogPixels);
end;

function TTextAttributes.GetPitch: TFontPitch;
var
  Format: CHARFORMAT2;
begin
  GetAttributes(Format);
  case (Format.bPitchAndFamily and $03) of
    DEFAULT_PITCH: Result := fpDefault;
    VARIABLE_PITCH: Result := fpVariable;
    FIXED_PITCH: Result := fpFixed;
  else
    Result := fpDefault;
  end;
end;

procedure TTextAttributes.SetPitch(Value: TFontPitch);
var
  Format: CHARFORMAT2;
begin
  InitFormat(Format);
  with Format do
  begin
    case Value of
      fpVariable: Format.bPitchAndFamily := VARIABLE_PITCH;
      fpFixed: Format.bPitchAndFamily := FIXED_PITCH;
    else
      Format.bPitchAndFamily := DEFAULT_PITCH;
    end;
  end;
  SetAttributes(Format);
end;

procedure TTextAttributes.Assign(Source: TPersistent);
begin
  if Source is TFont then
  begin
    Color := TFont(Source).Color;
    Name := TFont(Source).Name;
    Charset := TFont(Source).Charset;
    Style := TFont(Source).Style;
    Size := TFont(Source).Size;
    Pitch := TFont(Source).Pitch;
  end
  else if Source is TTextAttributes then
  begin
    Color := TTextAttributes(Source).Color;
    Name := TTextAttributes(Source).Name;
    Charset := TTextAttributes(Source).Charset;
    Style := TTextAttributes(Source).Style;
    Pitch := TTextAttributes(Source).Pitch;
  end
  else inherited Assign(Source);
end;

procedure TTextAttributes.AssignTo(Dest: TPersistent);
begin
  if Dest is TFont then
  begin
    TFont(Dest).Color := Color;
    TFont(Dest).Name := Name;
    TFont(Dest).Charset := Charset;
    TFont(Dest).Style := Style;
    TFont(Dest).Size := Size;
    TFont(Dest).Pitch := Pitch;
  end
  else if Dest is TTextAttributes then
  begin
    TTextAttributes(Dest).Color := Color;
    TTextAttributes(Dest).Name := Name;
    TTextAttributes(Dest).Charset := Charset;
    TTextAttributes(Dest).Style := Style;
    TTextAttributes(Dest).Pitch := Pitch;
  end
  else inherited AssignTo(Dest);
end;

{ TParaAttributes }

constructor TParaAttributes.Create(AOwner: TCustomRichEdit2);
begin
  inherited Create;
  RichEdit := AOwner;
end;

procedure TParaAttributes.InitPara(var Paragraph: PARAFORMAT2);
begin
  FillChar(Paragraph, SizeOf(PARAFORMAT2), 0);
  Paragraph.cbSize := SizeOf(PARAFORMAT2);
end;

procedure TParaAttributes.GetAttributes(var Paragraph: PARAFORMAT2);
begin
  InitPara(Paragraph);
  if RichEdit.HandleAllocated then
    SendMessage(RichEdit.Handle, EM_GETPARAFORMAT, 0, LPARAM(@Paragraph));
end;


procedure TParaAttributes.SetAttributes(var Paragraph: PARAFORMAT2);
begin
  RichEdit.HandleNeeded; { we REALLY need the handle for BiDi }
  if RichEdit.HandleAllocated then
  begin
    if RichEdit.UseRightToLeftAlignment then
      if Paragraph.wAlignment = PFA_LEFT then
        Paragraph.wAlignment := PFA_RIGHT
      else if Paragraph.wAlignment = PFA_RIGHT then
        Paragraph.wAlignment := PFA_LEFT;
    SendMessage(RichEdit.Handle, EM_SETPARAFORMAT, 0, LPARAM(@Paragraph));
  end;
end;

function TParaAttributes.GetAlignment: TAlignment;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := TAlignment(Paragraph.wAlignment - 1);
end;

procedure TParaAttributes.SetAlignment(Value: TAlignment);
var
  Paragraph: PARAFORMAT2;
begin
  InitPara(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_ALIGNMENT;
    wAlignment := Ord(Value) + 1;
  end;
  SetAttributes(Paragraph);
end;

function TParaAttributes.GetNumbering: TNumberingStyle2;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result.TypePuce  := TNsTypePuce(Paragraph.wNumbering);

  case Paragraph.wNumberingStyle of
    $000 : Result.CadrePuce := cpParentheseD  ;
    $100 : Result.CadrePuce := cpParenthesesGD;
    $200 : Result.CadrePuce := cpPoint        ;
    $300 : Result.CadrePuce := cpRien         ;
  end;
end;

procedure TParaAttributes.SetNumbering(Value: TNumberingStyle2);
var
  Paragraph: PARAFORMAT2;
begin
  case Value.TypePuce of
    tpBullet: if LeftIndent < 10 then LeftIndent := 10;
    tpNone: LeftIndent := 0;
  end;
  InitPara(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_NUMBERING or PFM_NUMBERINGSTART or PFM_NUMBERINGSTYLE;
    wNumbering := Ord(Value.TypePuce);
    wNumberingStart := 1;

    case Value.CadrePuce of
      cpParentheseD:   wNumberingStyle := $000;
      cpParenthesesGD: wNumberingStyle := $100;
      cpPoint:         wNumberingStyle := $200;
      cpRien:          wNumberingStyle := $300;
    end;
  end;
  SetAttributes(Paragraph);
end;

function TParaAttributes.GetFirstIndent: Longint;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := Paragraph.dxStartIndent div 20
end;

procedure TParaAttributes.SetFirstIndent(Value: Longint);
var
  Paragraph: PARAFORMAT2;
begin
  InitPara(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_STARTINDENT;
    dxStartIndent := Value * 20;
  end;
  SetAttributes(Paragraph);
end;

function TParaAttributes.GetLeftIndent: Longint;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := Paragraph.dxOffset div 20;
end;

procedure TParaAttributes.SetLeftIndent(Value: Longint);
var
  Paragraph: PARAFORMAT2;
begin
  InitPara(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_OFFSET;
    dxOffset := Value * 20;
  end;
  SetAttributes(Paragraph);
end;

function TParaAttributes.GetRightIndent: Longint;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := Paragraph.dxRightIndent div 20;
end;

procedure TParaAttributes.SetRightIndent(Value: Longint);
var
  Paragraph: PARAFORMAT2;
begin
  InitPara(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_RIGHTINDENT;
    dxRightIndent := Value * 20;
  end;
  SetAttributes(Paragraph);
end;

function TParaAttributes.GetTab(Index: Byte): Longint;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := (Paragraph.rgxTabs[Index] and $FFFFFF) div 20;
end;

function TParaAttributes.GetTabAlignment(Index: Byte): TTabAlignment;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := TTabAlignment((Paragraph.rgxTabs[Index] and $F000000) shr 24);
end;

procedure TParaAttributes.SetTab(Index: Byte; Value: Longint);
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  with Paragraph do
  begin
    rgxTabs[Index] := (rgxTabs[Index] and $FF000000) + Value * 20;
    dwMask := PFM_TABSTOPS;
    if cTabCount < Index then cTabCount := Index;
    SetAttributes(Paragraph);
  end;
end;

procedure TParaAttributes.SetTabAlignment(Index: Byte; Value: TTabAlignment);
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  with Paragraph do
  begin
    rgxTabs[Index] := (rgxTabs[Index] and not ($F000000)) or (Integer(Value) shl 24);
    dwMask := PFM_TABSTOPS;
    if cTabCount < Index then cTabCount := Index;
    SetAttributes(Paragraph);
  end;
end;


function TParaAttributes.GetTabCount: Integer;
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  Result := Paragraph.cTabCount;
end;

procedure TParaAttributes.SetTabCount(Value: Integer);
var
  Paragraph: PARAFORMAT2;
begin
  GetAttributes(Paragraph);
  with Paragraph do
  begin
    dwMask := PFM_TABSTOPS;
    cTabCount := Value;
    SetAttributes(Paragraph);
  end;
end;

procedure TParaAttributes.Assign(Source: TPersistent);
var
  I: Integer;
begin
  if Source is TParaAttributes then
  begin
    Alignment := TParaAttributes(Source).Alignment;
    FirstIndent := TParaAttributes(Source).FirstIndent;
    LeftIndent := TParaAttributes(Source).LeftIndent;
    RightIndent := TParaAttributes(Source).RightIndent;
    SetNumbering(TParaAttributes(Source).GetNumbering());
    for I := 0 to MAX_TAB_STOPS - 1 do
      Tab[I] := TParaAttributes(Source).Tab[I];
  end
  else inherited Assign(Source);
end;

{ TRichEdit2Strings }

const
  ReadError = $0001;
  WriteError = $0002;
  NoError = $0000;

type
  TSelection = record
    StartPos, EndPos: Integer;
  end;

  TRichEdit2Strings = class(TStrings)
  private
    RichEdit: TCustomRichEdit2;
    FPlainText: Boolean;
    FConverter: TConversion;
    procedure EnableChange(const Value: Boolean);
  protected
    function Get(Index: Integer): string; override;
    function GetCount: Integer; override;
    procedure Put(Index: Integer; const S: string); override;
    procedure SetUpdateState(Updating: Boolean); override;
    procedure SetTextStr(const Value: string); override;
  public
    destructor Destroy; override;
    procedure Clear; override;
    procedure AddStrings(Strings: TStrings); override;
    procedure Delete(Index: Integer); override;
    procedure Insert(Index: Integer; const S: string); override;
    procedure LoadFromFile(const FileName: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToFile(const FileName: string); override;
    procedure SaveToStream(Stream: TStream); override;
    property PlainText: Boolean read FPlainText write FPlainText;
  end;

destructor TRichEdit2Strings.Destroy;
begin
  FConverter.Free;
  inherited Destroy;
end;

procedure TRichEdit2Strings.AddStrings(Strings: TStrings);
var
  SelChange: TNotifyEvent;
begin
  SelChange := RichEdit.OnSelectionChange;
  RichEdit.OnSelectionChange := nil;
  try
    inherited AddStrings(Strings);
  finally
    RichEdit.OnSelectionChange := SelChange;
  end;
end;

function TRichEdit2Strings.GetCount: Integer;
begin
  Result := SendMessage(RichEdit.Handle, EM_GETLINECOUNT, 0, 0);
  if SendMessage(RichEdit.Handle, EM_LINELENGTH, SendMessage(RichEdit.Handle,
    EM_LINEINDEX, Result - 1, 0), 0) = 0 then Dec(Result);
end;

function TRichEdit2Strings.Get(Index: Integer): string;
var
{$IFNDEF RICHEDIT_UNICODE}
  Text: array[0..4095] of CHAR;
{$ELSE}
  Text: array[0..4095] of WCHAR;
{$ENDIF}
  L: Integer;
begin
  Word((@Text)^) := 4095;
{$IFNDEF RICHEDIT_UNICODE}
  L := SendMessage(RichEdit.Handle, EM_GETLINE, Index, Longint(@Text));
{$ELSE}
  L := SendMessageW(RichEdit.Handle, EM_GETLINE, Index, Longint(@Text));
{$ENDIF}
  if (Text[L - 1] = #13)
     then Dec(L, 1);
  SetString(Result, Text, L);
end;

procedure TRichEdit2Strings.Put(Index: Integer; const S: string);
var
  Selection: TCharRange;
{$IFDEF RICHEDIT_UNICODE}
  ValueW : WideString;
{$ENDIF}
begin
  if Index >= 0 then
  begin
    Selection.cpMin := SendMessage(RichEdit.Handle, EM_LINEINDEX, Index, 0);
    if Selection.cpMin <> -1 then
    begin
      Selection.cpMax := Selection.cpMin +
        SendMessage(RichEdit.Handle, EM_LINELENGTH, Selection.cpMin, 0);
      SendMessage(RichEdit.Handle, EM_EXSETSEL, 0, Longint(@Selection));
{$IFNDEF RICHEDIT_UNICODE}
      SendMessage(RichEdit.Handle, EM_REPLACESEL, 0, Longint(PChar(S)));
{$ELSE}
      ValueW := S;
      if ((not SendMessageW(RichEdit.Handle, EM_REPLACESEL, 0, Longint(PWChar(ValueW))))
          and GetLastError() = 120) then//Pour windows 98
         begin
         SendMessage(RichEdit.Handle, EM_REPLACESEL, 0, Longint(PWChar(ValueW)));
         end;
{$ENDIF}
    end;
  end;
end;

procedure TRichEdit2Strings.Insert(Index: Integer; const S: string);
var
  L: Integer;
  Selection: TCharRange;
  Fmt: PChar;
{$IFNDEF RICHEDIT_UNICODE}
  Str: string;
{$ELSE}
  Str: WideString;
  ValueW : WideString;
{$ENDIF}
begin
  if Index >= 0 then
  begin
    Selection.cpMin := SendMessage(RichEdit.Handle, EM_LINEINDEX, Index, 0);
    if Selection.cpMin >= 0 then Fmt := '%s'#13
    else begin
      Selection.cpMin :=
        SendMessage(RichEdit.Handle, EM_LINEINDEX, Index - 1, 0);
      if Selection.cpMin < 0 then Exit;
      L := SendMessage(RichEdit.Handle, EM_LINELENGTH, Selection.cpMin, 0);
      if L = 0 then Exit;
      Inc(Selection.cpMin, L);
      Fmt := #13'%s';
    end;
    Selection.cpMax := Selection.cpMin;
    SendMessage(RichEdit.Handle, EM_EXSETSEL, 0, Longint(@Selection));
    Str := Format(Fmt, [S]);

{$IFNDEF RICHEDIT_UNICODE}
    SendMessage(RichEdit.Handle, EM_REPLACESEL, 0, LongInt(PChar(Str)));
{$ELSE}
    ValueW := Str;
    if ((not SendMessageW(RichEdit.Handle, EM_REPLACESEL, Longint(TRUE), Longint(PWChar(ValueW))))
        and GetLastError() = 120) then//Pour windows 98
       begin
       SendMessage(RichEdit.Handle, EM_REPLACESEL, 0, Longint(PWChar(ValueW)));
       end;
{$ENDIF}

    if RichEdit.SelStart <> (Selection.cpMax + Length(Str)) then
      raise EOutOfResources.Create(sRichEditInsertError);
  end;
end;

procedure TRichEdit2Strings.Delete(Index: Integer);
const
  Empty: PChar = '\0\0';
var
  Selection: TCharRange;
begin
  if Index < 0 then Exit;
  Selection.cpMin := SendMessage(RichEdit.Handle, EM_LINEINDEX, Index, 0);
  if Selection.cpMin <> -1 then
  begin
    Selection.cpMax := SendMessage(RichEdit.Handle, EM_LINEINDEX, Index + 1, 0);
    if Selection.cpMax = -1 then                 
      Selection.cpMax := Selection.cpMin +
        SendMessage(RichEdit.Handle, EM_LINELENGTH, Selection.cpMin, 0);
    SendMessage(RichEdit.Handle, EM_EXSETSEL, Longint(TRUE), Longint(@Selection));
    SendMessage(RichEdit.Handle, EM_REPLACESEL, Longint(TRUE), Longint(Empty));
  end;
end;

procedure TRichEdit2Strings.Clear;
begin
  RichEdit.Clear;
end;

procedure TRichEdit2Strings.SetUpdateState(Updating: Boolean);
begin
  if RichEdit.Showing then
    SendMessage(RichEdit.Handle, WM_SETREDRAW, Ord(not Updating), 0);
  if not Updating then begin
    RichEdit.Refresh;
    RichEdit.Perform(CM_TEXTCHANGED, 0, 0);
  end;
end;

procedure TRichEdit2Strings.EnableChange(const Value: Boolean);
var
  EventMask: Longint;
begin
  with RichEdit do
  begin
    if Value then
      EventMask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0) or ENM_CHANGE
    else
      EventMask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0) and not ENM_CHANGE;
    SendMessage(Handle, EM_SETEVENTMASK, 0, EventMask);
  end;
end;

procedure TRichEdit2Strings.SetTextStr(const Value: string);
begin
  EnableChange(False);
  try
    inherited SetTextStr(Value);
  finally
    EnableChange(True);
  end;
end;

function AdjustLineBreaks(Dest, Source: PChar): Integer; assembler;
asm
        PUSH    ESI
        PUSH    EDI
        MOV     EDI,EAX
        MOV     ESI,EDX
        MOV     EDX,EAX
        CLD
@@1:    LODSB
@@2:    OR      AL,AL
        JE      @@4
        CMP     AL,0AH
        JE      @@3
        STOSB
        CMP     AL,0DH
        JNE     @@1
        MOV     AL,0AH
        STOSB
        LODSB
        CMP     AL,0AH
        JE      @@1
        JMP     @@2
@@3:    MOV     EAX,0A0DH
        STOSW
        JMP     @@1
@@4:    STOSB
        LEA     EAX,[EDI-1]
        SUB     EAX,EDX
        POP     EDI
        POP     ESI
end;

function StreamSave(dwCookie: Longint; pbBuff: PByte;
  cb: Longint; var pcb: Longint): Longint; stdcall;
var
  StreamInfo: PRichEditStreamInfo;
begin
  Result := NoError;
  StreamInfo := PRichEditStreamInfo(Pointer(dwCookie));
  try
    pcb := 0;
    if StreamInfo^.Converter <> nil then
      pcb := StreamInfo^.Converter.ConvertWriteStream(StreamInfo^.Stream, PChar(pbBuff), cb);
  except
    Result := WriteError;
  end;
end;

function StreamLoad(dwCookie: Longint; pbBuff: PByte;
  cb: Longint; var pcb: Longint): Longint; stdcall;
var
  Buffer, pBuff: PChar;
  StreamInfo: PRichEditStreamInfo;
begin
  Result := NoError;
  StreamInfo := PRichEditStreamInfo(Pointer(dwCookie));
  Buffer := StrAlloc(cb + 1);
  try
    cb := cb div 2;
    pcb := 0;
    pBuff := Buffer + cb;
    try
      if StreamInfo^.Converter <> nil then
        pcb := StreamInfo^.Converter.ConvertReadStream(StreamInfo^.Stream, pBuff, cb);
      if pcb > 0 then
      begin
        pBuff[pcb] := #0;
        if pBuff[pcb - 1] = #13 then pBuff[pcb - 1] := #0;
        pcb := AdjustLineBreaks(Buffer, pBuff);
        Move(Buffer^, pbBuff^, pcb);
      end;
    except
      Result := ReadError;
    end;
  finally
    StrDispose(Buffer);
  end;
end;


procedure TRichEdit2Strings.LoadFromStream(Stream: TStream);
var
  EditStream: TEditStream;
  Position: Longint;
  TextType: Longint;
  StreamInfo: TRichEditStreamInfo;
  Converter: TConversion;
begin
  StreamInfo.Stream := Stream;
  if FConverter <> nil then Converter := FConverter
  else Converter := RichEdit.DefaultConverter.Create;
  StreamInfo.Converter := Converter;
  try
    with EditStream do
    begin
      dwCookie := LongInt(Pointer(@StreamInfo));
      pfnCallBack := @StreamLoad;
      dwError := 0;
    end;
    Position := Stream.Position;
    if PlainText then TextType := SF_TEXT
    else TextType := SF_RTF;
    SendMessage(RichEdit.Handle, EM_STREAMIN, TextType, Longint(@EditStream));
    if (TextType = SF_RTF) and (EditStream.dwError <> 0) then
    begin
      Stream.Position := Position;
      if PlainText then TextType := SF_RTF
      else TextType := SF_TEXT;
      SendMessage(RichEdit.Handle, EM_STREAMIN, TextType, Longint(@EditStream));
      if EditStream.dwError <> 0 then
        raise EOutOfResources.Create(sRichEditLoadFail);
    end;
  finally
    if FConverter = nil then Converter.Free;
  end;
end;

procedure TRichEdit2Strings.SaveToStream(Stream: TStream);
var
  EditStream: TEditStream;
  TextType: Longint;
  StreamInfo: TRichEditStreamInfo;
  Converter: TConversion;
begin
  if FConverter <> nil then Converter := FConverter
  else Converter := RichEdit.DefaultConverter.Create;
  StreamInfo.Stream := Stream;
  StreamInfo.Converter := Converter;
  try
    with EditStream do
    begin
      dwCookie := LongInt(Pointer(@StreamInfo));
      pfnCallBack := @StreamSave;
      dwError := 0;
    end;
    if PlainText then TextType := SF_TEXT
    else TextType := SF_RTF;
    SendMessage(RichEdit.Handle, EM_STREAMOUT, TextType, Longint(@EditStream));
    if EditStream.dwError <> 0 then
      raise EOutOfResources.Create(sRichEditSaveFail);
  finally
    if FConverter = nil then Converter.Free;
  end;
end;

procedure TRichEdit2Strings.LoadFromFile(const FileName: string);
var
  Ext: string;
  Convert: PConversionFormat;
begin
  Ext := AnsiLowerCaseFileName(ExtractFileExt(Filename));
  System.Delete(Ext, 1, 1);
  Convert := ConversionFormatList;
  while Convert <> nil do
    with Convert^ do
      if Extension <> Ext then Convert := Next
      else Break;
  if Convert = nil then
    Convert := @TextConversionFormat;
  if FConverter = nil then FConverter := Convert^.ConversionClass.Create;
  try
    inherited LoadFromFile(FileName);
  except
    FConverter.Free;
    FConverter := nil;
    raise;
  end;
  RichEdit.DoSetMaxLength($7FFFFFF0);
end;

procedure TRichEdit2Strings.SaveToFile(const FileName: string);
var
  Ext: string;
  Convert: PConversionFormat;
begin
  Ext := AnsiLowerCaseFileName(ExtractFileExt(Filename));
  System.Delete(Ext, 1, 1);
  Convert := ConversionFormatList;
  while Convert <> nil do
    with Convert^ do
      if Extension <> Ext then Convert := Next
      else Break;
  if Convert = nil then
    Convert := @TextConversionFormat;
  if FConverter = nil then FConverter := Convert^.ConversionClass.Create;
  try
    inherited SaveToFile(FileName);
  except
    FConverter.Free;
    FConverter := nil;
    raise;
  end;
end;

{ TRichEdit2 }

constructor TCustomRichEdit2.Create(AOwner: TComponent);
var
  DC: HDC;
begin
  inherited Create(AOwner);
  FSelAttributes := TTextAttributes.Create(TCustomRichEdit2(Self), atSelected);
  FDefAttributes := TTextAttributes.Create(TCustomRichEdit2(Self), atDefaultText);
  FParagraph := TParaAttributes.Create(Self);
  FRichEditStrings := TRichEdit2Strings.Create;
  TRichEdit2Strings(FRichEditStrings).RichEdit := Self;
  TabStop := True;
  Width := 185;
  Height := 89;
  AutoSize := False;
  DoubleBuffered := False;
  FHideSelection := True;
  HideScrollBars := True;
  DC := GetDC(0);
  FScreenLogPixels := GetDeviceCaps(DC, LOGPIXELSY);
  DefaultConverter := TConversion;
  ReleaseDC(0, DC);
  FOldParaAlignment := Alignment;
  Perform(CM_PARENTBIDIMODECHANGED, 0, 0);
end;

destructor TCustomRichEdit2.Destroy;
begin
  FSelAttributes.Free;
  FDefAttributes.Free;
  FParagraph.Free;
  FRichEditStrings.Free;
  FMemStream.Free;
  inherited Destroy;
end;

procedure TCustomRichEdit2.Clear;
begin
  inherited Clear;
  Modified := False;
end;

procedure TCustomRichEdit2.CreateParams(var Params: TCreateParams);
const
  HideScrollBars: array[Boolean] of DWORD = (ES_DISABLENOSCROLL, 0);
  HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
begin
{$IFNDEF RICHEDIT_UNICODE}
  if FRichEdit20Module = 0 then
  begin
    FRichEdit20Module := LoadLibrary('RICHED20.DLL');
    if FRichEdit20Module <= HINSTANCE_ERROR then FRichEdit20Module := 0;
  end;
{$ELSE}
  if FMsftEditModule = 0 then
  begin
    FMsftEditModule := LoadLibrary('MsftEdit.DLL');
    if FMsftEditModule <= HINSTANCE_ERROR then FMsftEditModule := 0;
  end;
{$ENDIF}
  inherited CreateParams(Params);

{$IFNDEF RICHEDIT_UNICODE}
  CreateSubClass(Params, RICHEDIT_CLASS); //Passage en RichEdit 2.0
{$ELSE}
  CreateSubClass(Params, 'RichEdit50W'); //Passage en RichEdit 5.0
{$ENDIF}

  with Params do
  begin
    Style := Style or HideScrollBars[FHideScrollBars] or
      HideSelections[HideSelection];
    WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
  end;
end;

procedure TCustomRichEdit2.CreateWnd;
var
  Plain, DesignMode, WasModified: Boolean;
begin
  WasModified := inherited Modified;
  inherited CreateWnd;
  if (SysLocale.FarEast) and not (SysLocale.PriLangID = LANG_JAPANESE) then
    Font.Charset := GetDefFontCharSet;
  SendMessage(Handle, EM_SETEVENTMASK, 0,
    ENM_CHANGE or ENM_SELCHANGE or ENM_REQUESTRESIZE or
    ENM_PROTECTED);

//  SendMessage(Handle, EM_SETTEXTMODE, TM_RICHTEXT or TM_MULTILEVELUNDO or TM_SINGLECODEPAGE, 0);

  SendMessage(Handle, EM_SETBKGNDCOLOR, 0, ColorToRGB(Color));
  SendMessage(Handle, EM_SETTYPOGRAPHYOPTIONS, TO_ADVANCEDTYPOGRAPHY, 1); //Permet d'afficher correctement les textes justifiés et autres évolutions.
  if FMemStream <> nil then
  begin
    Plain := PlainText;
    FMemStream.ReadBuffer(DesignMode, sizeof(DesignMode));
    PlainText := DesignMode;
    try
      Lines.LoadFromStream(FMemStream);
      FMemStream.Free;
      FMemStream := nil;
    finally
      PlainText := Plain;
    end;
  end;
  Modified := WasModified;
end;

procedure TCustomRichEdit2.DestroyWnd;
var
  Plain, DesignMode: Boolean;
begin
  FModified := Modified;
  FMemStream := TMemoryStream.Create;
  Plain := PlainText;
  DesignMode := (csDesigning in ComponentState);
  PlainText := DesignMode;
  FMemStream.WriteBuffer(DesignMode, sizeof(DesignMode));
  try
    Lines.SaveToStream(FMemStream);
    FMemStream.Position := 0;
  finally
    PlainText := Plain;
  end;
  inherited DestroyWnd;
end;

procedure TCustomRichEdit2.WMNCDestroy(var Message: TWMNCDestroy);
begin
  inherited;
end;

procedure TCustomRichEdit2.WMSetFont(var Message: TWMSetFont);
begin
  FDefAttributes.Assign(Font);
end;

procedure TCustomRichEdit2.WMRButtonUp(var Message: TWMRButtonUp);
begin
  // RichEd20 does not pass the WM_RBUTTONUP message to defwndproc,
  // so we get no WM_CONTEXTMENU message.  Simulate message here.
  if Win32MajorVersion < 5 then
    Perform(WM_CONTEXTMENU, Handle, LParam(PointToSmallPoint(
      ClientToScreen(SmallPointToPoint(Message.Pos)))));
  inherited;
end;

function TCustomRichEdit2.GetText: string;
begin
  Result := inherited Text;
end;

procedure TCustomRichEdit2.SetText(Value: string);
{$IFDEF RICHEDIT_UNICODE}
var
  ValueW : WideString;
{$ENDIF}

begin
{$IFNDEF RICHEDIT_UNICODE}
  SendMessage(Handle, WM_SETTEXT, 0, Longint(PChar(Value)));
{$ELSE}
  ValueW := Value;
  if (SendMessageW(Handle, WM_SETTEXT, 0, Longint(PWChar(ValueW))) = 0) then //Pour windows 98
     begin
     SendMessage(Handle, WM_SETTEXT, 0, Longint(PWChar(ValueW)));
     end;
{$ENDIF}
  SendMessage(Handle, CM_TEXTCHANGED, 0, 0);
end;


procedure TCustomRichEdit2.CMFontChanged(var Message: TMessage);
begin
  FDefAttributes.Assign(Font);
end;

procedure TCustomRichEdit2.DoSetMaxLength(Value: Integer);
begin
  SendMessage(Handle, EM_EXLIMITTEXT, 0, Value);
end;

function TCustomRichEdit2.GetCaretPos;
var
  CharRange: TCharRange;
begin
  SendMessage(Handle, EM_EXGETSEL, 0, LongInt(@CharRange));
  Result.X := CharRange.cpMax;
  Result.Y := SendMessage(Handle, EM_EXLINEFROMCHAR, 0, Result.X);
  Result.X := Result.X - SendMessage(Handle, EM_LINEINDEX, -1, 0);
end;

function TCustomRichEdit2.GetSelLength: Integer;
var
  CharRange: TCharRange;
begin
  SendMessage(Handle, EM_EXGETSEL, 0, Longint(@CharRange));
  Result := CharRange.cpMax - CharRange.cpMin;
end;

function TCustomRichEdit2.GetSelStart: Integer;
var
  CharRange: TCharRange;
begin
  SendMessage(Handle, EM_EXGETSEL, 0, Longint(@CharRange));
  Result := CharRange.cpMin;
end;

function TCustomRichEdit2.GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer;
var
  S: string;
begin
  S := GetSelText;
  Result := Length(S);
  if BufSize < Length(S) then Result := BufSize;
  StrPLCopy(Buffer, S, Result);
end;

function TCustomRichEdit2.GetSelText: string;
var
  Length: Integer;
{$IFDEF RICHEDIT_UNICODE}
  ChaineW : WideString;
{$ENDIF}

begin
{$IFNDEF RICHEDIT_UNICODE}
  SetLength(Result, GetSelLength + 1);
  Length := SendMessage(Handle, EM_GETSELTEXT, 0, Longint(PChar(Result)));
  SetLength(Result, Length);
{$ELSE}
  SetLength(ChaineW, GetSelLength + 1);
  Length := SendMessage(Handle, EM_GETSELTEXT, 0, Longint(PWChar(ChaineW)));
  SetLength(ChaineW, Length);
  Result := WideCharToString(PWChar(ChaineW));
{$ENDIF}
end;

procedure TCustomRichEdit2.SetSelText(const Value: String);
{$IFDEF RICHEDIT_UNICODE}
var
   ValueW : WideString;
{$ENDIF}
begin
{$IFNDEF RICHEDIT_UNICODE}
  SendMessage(Handle, EM_REPLACESEL, Longint(TRUE), Longint(PChar(Value)));
{$ELSE}
  ValueW := Value;
  if ((not SendMessageW(Handle, EM_REPLACESEL, Longint(TRUE), Longint(PWChar(ValueW))))
      and GetLastError() = 120) then//Pour windows 98
     begin
     SendMessage(Handle, EM_REPLACESEL, Longint(TRUE), Longint(PWChar(ValueW)));
     end;
{$ENDIF}
end;

procedure TCustomRichEdit2.CMBiDiModeChanged(var Message: TMessage);
var
  AParagraph: PARAFORMAT2;
begin
  HandleNeeded; { we REALLY need the handle for BiDi }
  inherited;
  Paragraph.GetAttributes(AParagraph);
  AParagraph.dwMask := PFM_ALIGNMENT;
  AParagraph.wAlignment := Ord(Alignment) + 1;
  Paragraph.SetAttributes(AParagraph);
end;

procedure TCustomRichEdit2.SetHideScrollBars(Value: Boolean);
begin
  if HideScrollBars <> Value then
  begin
    FHideScrollBars := value;
    RecreateWnd;
  end;
end;

procedure TCustomRichEdit2.SetHideSelection(Value: Boolean);
begin
  if HideSelection <> Value then
  begin
    FHideSelection := Value;
    SendMessage(Handle, EM_HIDESELECTION, Ord(HideSelection), LongInt(True));
  end;
end;

procedure TCustomRichEdit2.SetSelAttributes(Value: TTextAttributes);
begin
  SelAttributes.Assign(Value);
end;

procedure TCustomRichEdit2.SetSelLength(Value: Integer);
var
  CharRange: TCharRange;
begin
  SendMessage(Handle, EM_EXGETSEL, 0, Longint(@CharRange));
  CharRange.cpMax := CharRange.cpMin + Value;
  SendMessage(Handle, EM_EXSETSEL, 0, Longint(@CharRange));
  SendMessage(Handle, EM_SCROLLCARET, 0, 0);
end;

procedure TCustomRichEdit2.SetDefAttributes(Value: TTextAttributes);
begin
  DefAttributes.Assign(Value);
end;

function TCustomRichEdit2.GetPlainText: Boolean;
begin
  Result := TRichEdit2Strings(Lines).PlainText;
end;

procedure TCustomRichEdit2.SetPlainText(Value: Boolean);
begin
  TRichEdit2Strings(Lines).PlainText := Value;
end;

procedure TCustomRichEdit2.CMColorChanged(var Message: TMessage);
begin
  inherited;
  SendMessage(Handle, EM_SETBKGNDCOLOR, 0, ColorToRGB(Color))
end;

procedure TCustomRichEdit2.SetRichEditStrings(Value: TStrings);
begin
  FRichEditStrings.Assign(Value);
end;

procedure TCustomRichEdit2.SetSelStart(Value: Integer);
var
  CharRange: TCharRange;
begin
  CharRange.cpMin := Value;
  CharRange.cpMax := Value;
  SendMessage(Handle, EM_EXSETSEL, 0, Longint(@CharRange));
end;

procedure TCustomRichEdit2.Print(const Caption: string);
var
  Range: TFormatRange;
  LastChar, MaxLen, LogX, LogY, OldMap: Integer;
  SaveRect: TRect;
begin
  FillChar(Range, SizeOf(TFormatRange), 0);
  with Printer, Range do
  begin
    Title := Caption;
    BeginDoc;
    hdc := Handle;
    hdcTarget := hdc;
    LogX := GetDeviceCaps(Handle, LOGPIXELSX);
    LogY := GetDeviceCaps(Handle, LOGPIXELSY);
    if IsRectEmpty(PageRect) then
    begin
      rc.right := PageWidth * 1440 div LogX;
      rc.bottom := PageHeight * 1440 div LogY;
    end
    else begin
      rc.left := PageRect.Left * 1440 div LogX;
      rc.top := PageRect.Top * 1440 div LogY;
      rc.right := PageRect.Right * 1440 div LogX;
      rc.bottom := PageRect.Bottom * 1440 div LogY;
    end;
    rcPage := rc;
    SaveRect := rc;
    LastChar := 0;
    MaxLen := GetTextLen;
    chrg.cpMax := -1;
    // ensure printer DC is in text map mode
    OldMap := SetMapMode(hdc, MM_TEXT);
    SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);    // flush buffer
    try
      repeat
        rc := SaveRect;
        chrg.cpMin := LastChar;
        LastChar := SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range));
        if (LastChar < MaxLen) and (LastChar <> -1) then NewPage;
      until (LastChar >= MaxLen) or (LastChar = -1);
      EndDoc;
    finally
      SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);  // flush buffer
      SetMapMode(hdc, OldMap);       // restore previous map mode
    end;
  end;
end;

var
  Painting: Boolean = False;

procedure TCustomRichEdit2.WMPaint(var Message: TWMPaint);
var
  R, R1: TRect;
begin
  if GetUpdateRect(Handle, R, True) then
  begin
    with ClientRect do R1 := Rect(Right - 3, Top, Right, Bottom);
    if IntersectRect(R, R, R1) then InvalidateRect(Handle, @R1, True);
  end;
  if Painting then
    Invalidate
  else begin
    Painting := True;
    try
      inherited;
    finally
      Painting := False;
    end;
  end;
end;

procedure TCustomRichEdit2.WMSetCursor(var Message: TWMSetCursor);
var
  P: TPoint;
begin
  inherited;
  if Message.Result = 0 then
  begin
    Message.Result := 1;
    GetCursorPos(P);
    with PointToSmallPoint(P) do
      case Perform(WM_NCHITTEST, 0, MakeLong(X, Y)) of
        HTVSCROLL,
        HTHSCROLL:
          Windows.SetCursor(Screen.Cursors[crArrow]);
        HTCLIENT:
          Windows.SetCursor(Screen.Cursors[crIBeam]);
      end;
  end;
end;

procedure TCustomRichEdit2.CNNotify(var Message: TWMNotify);
begin
  with Message do
    case NMHdr^.code of
      EN_SELCHANGE: SelectionChange;
      EN_REQUESTRESIZE: RequestSize(PReqSize(NMHdr)^.rc);
      EN_SAVECLIPBOARD:
        with PENSaveClipboard(NMHdr)^ do
          if not SaveClipboard(cObjectCount, cch) then Result := 1;
      EN_PROTECTED:
        with PENProtected(NMHdr)^.chrg do
          if not ProtectChange(cpMin, cpMax) then Result := 1;
    end;
end;

function TCustomRichEdit2.SaveClipboard(NumObj, NumChars: Integer): Boolean;
begin
  Result := True;
  if Assigned(OnSaveClipboard) then OnSaveClipboard(Self, NumObj, NumChars, Result);
end;

function TCustomRichEdit2.ProtectChange(StartPos, EndPos: Integer): Boolean;
begin
  Result := False;
  if Assigned(OnProtectChange) then OnProtectChange(Self, StartPos, EndPos, Result);
end;

procedure TCustomRichEdit2.SelectionChange;
begin
  if Assigned(OnSelectionChange) then OnSelectionChange(Self);
end;

procedure TCustomRichEdit2.RequestSize(const Rect: TRect);
begin
  if Assigned(OnResizeRequest) then OnResizeRequest(Self, Rect);
end;

{$IFNDEF RICHEDIT_UNICODE}
function TCustomRichEdit2.FindTextA(const SearchStr: string;
  StartPos, LengthText: Integer; Options: TSearchTypes): Integer;
var
  Find: TFindText;
  Flags: Integer;

begin
 with Find.chrg do
  begin
    cpMin := StartPos;
    cpMax := cpMin + LengthText;
  end;
{
Fonction : Le FindText a été modifié car son fonctionnement change lorsque l'on utilise
           un RichEdit2.
           Avec un RichEdit1 : La recherche commence à la fin de la sélection et se termine à la fin du document.
           Avec un RichEdit2, par défaut : La recherche commence à la fin de la sélection et remonte jusqu'au début du document.
                                           On doit activer le flag FR_DOWN pour que le fonctionnement soit identique dans les deux cas.
}
  Flags := FR_DOWN;
  if stWholeWord in Options then Flags := Flags or FT_WHOLEWORD;
  if stMatchCase in Options then Flags := Flags or FT_MATCHCASE;
  Find.lpstrText := PChar(SearchStr);
  Result := SendMessage(Handle, EM_FINDTEXT, Flags, LongInt(@Find));
end;
{$ELSE}
function TCustomRichEdit2.FindTextA(const SearchStr: string;
  StartPos, LengthText: Integer; Options: TSearchTypes): Integer;
var
  Find: TFindTextW;
  Flags: Integer;
  Destination:  WideString;
begin
 with Find.chrg do
  begin
    cpMin := StartPos;
    cpMax := cpMin + LengthText;
  end;
  Flags := FR_DOWN;
  if stWholeWord in Options then Flags := Flags or FT_WHOLEWORD;
  if stMatchCase in Options then Flags := Flags or FT_MATCHCASE;

  SetLength(Destination, Length(SearchStr));
  StringToWideChar(SearchStr, PWChar(Destination), Length(Destination)+1);
  Find.lpstrText := PWChar(Destination);
  //(WM_USER + 123) équivaut à EM_FINDTEXTW
  Result := SendMessage(Handle, (WM_USER + 123), Flags, LongInt(@Find));
end;
{$ENDIF}

procedure AppendConversionFormat(const Ext: string; AClass: TConversionClass);
var
  NewRec: PConversionFormat;
begin
  New(NewRec);
  with NewRec^ do
  begin
    Extension := AnsiLowerCaseFileName(Ext);
    ConversionClass := AClass;
    Next := ConversionFormatList;
  end;
  ConversionFormatList := NewRec;
end;

class procedure TCustomRichEdit2.RegisterConversionFormat(const AExtension: string;
  AConversionClass: TConversionClass);
begin
  AppendConversionFormat(AExtension, AConversionClass);
end;



initialization
{$IFNDEF RICHEDIT_UNICODE}
  FRichEdit20Module := 0;
{$ELSE}
  FMsftEditModule := 0;
{$ENDIF}

finalization
{$IFNDEF RICHEDIT_UNICODE}
  if FRichEdit20Module <> 0 then
     begin
     FreeLibrary(FRichEdit20Module);
     FRichEdit20Module := 0;
     end;
{$ELSE}
  if FMsftEditModule <> 0 then
     begin
     FreeLibrary(FMsftEditModule);
     FMsftEditModule := 0;
     end;
{$ENDIF}
end.
